package synchronicity.util;

import java.util.Date;
import java.util.Vector;

import synchronicity.agent.ScheduleCollectorAgent;
import synchronicity.agent.SchedulerAgent;
import synchronicity.host.SchedulerHost;

/**
 * Utility class for orchestrating a meeting.
 */
public class MeetingScheduler implements Runnable {

	private Date startDate, endDate;

	private long length;

	private String title;

	private SchedulerHost host;

	public class MeetingResult {
		public boolean complete = false;

		public Vector failedHosts = new Vector();

		public Vector successfulHosts = new Vector();

		public Vector offlineHosts = new Vector();
	}

	private MeetingResult result;

	public MeetingScheduler(Date startDate, Date endDate, long length,
			String title, SchedulerHost host) {
		super();
		this.startDate = startDate;
		this.endDate = endDate;
		this.length = length;
		this.title = title;
		this.host = host;
		result = new MeetingResult();
	}

	/**
	 * Schedule a meeting. The process is as follows:
	 * <li>Let a ScheduleCollectorAgent traverse the graph to find all
	 * unavailable times.
	 * <li>Calculate a meeting time that works for everyone
	 * <li>Send a SchedulerAgent out to request the meeting
	 * <li>
	 */
	public void run() {
		synchronized (result) {
			System.out.println("Starting scheduling..");
			Range timeRange = new Range(startDate.getTime(), endDate.getTime());
			// Find busy times
			ScheduleCollectorAgent collectorAgent = new ScheduleCollectorAgent(
					timeRange, new RangeSet());
			collectorAgent = (ScheduleCollectorAgent) host
					.traverse(collectorAgent);

			System.out.println("Collection finished!");
			// Perform calculation
			Range resultRange = findMeetingTime(length, timeRange,
					collectorAgent.getBusyTimes());
			if (resultRange != null) {
				// Send meeting request
				SchedulerAgent schedulerAgent = new SchedulerAgent(resultRange,
						title);
				schedulerAgent = (SchedulerAgent) host.traverse(schedulerAgent);

				result.failedHosts = schedulerAgent.getFailedHosts();
				result.successfulHosts = schedulerAgent.getSuccessfulHosts();

				System.out.println("Hosts that rejected the meeting :"
						+ result.failedHosts.size());
				System.out.println("Hosts that accepted the meeting :"
						+ result.successfulHosts.size());
			} else {
				System.err.println("Error: No one can make the meeting!");
			}
			// TODO offline hosts
			result.complete = true;
			result.notifyAll();
		}
	}

	private Range findMeetingTime(long length, Range timeRange,
			RangeSet busyTimes) {
		RangeSet workableTimeRanges = MeetingCriteria.getInstance()
				.getAcceptableRanges(timeRange);
		for (int ii = 0; ii < workableTimeRanges.getRanges().size(); ii++) {
			Range possibleResult = SchedulerUtil.getMeetingTime(length,
					(Range) workableTimeRanges.getRanges().elementAt(ii),
					busyTimes);
			if (possibleResult != null) {
				return possibleResult;
			}
		}
		return null;
	}

	public MeetingResult getMeetingResult() {
		synchronized (result) {
			waitForResult();
			return result;
		}
	}

	private void waitForResult() {
		synchronized (result) {
			while (!result.complete) {
				try {
					result.wait();
				} catch (InterruptedException ie) {
				}
			}
		}
	}
}
